package constructdata;

import codetree.*;

import japa.parser.JavaParser;
import japa.parser.ast.stmt.*;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.body.*;
import japa.parser.ast.Node;
import japa.parser.ast.expr.VariableDeclarationExpr;
import treeview.TreeView;
import treeview.DisplayTreeView;

import java.io.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;


/**
 * Created by chen chi on 18/2/7.
 */
public class JudgeCodeTree {
    public String judgeCodeTree(int count, String filePath, boolean isFilePath, List<String> jdkList, boolean holeFlag, String globalPath,
                                     List<String> gloveVocabList, List<String> stopWordsList) {
        JapaAst japaAst = new JapaAst(true);
        List<String> tempList = new ArrayList<>();
        CompilationUnit cu = new CompilationUnit();
        try {
            if (isFilePath) {
                cu = JavaParser.parse(new File(filePath));
            } else {
                InputStream in = new ByteArrayInputStream(filePath.getBytes());
                cu = JavaParser.parse(in);
            }
            //tempList = jdtAst.parse(filePath, isFilePath);
            tempList = japaAst.parse(cu);
        } catch (Exception e) {
            return null;
        } catch (Error e) {
            return null;
        }
        //如果Import的包中带有*号，那么得到含有*号的这个import
        List importList = cu.getImports();
        List<String> starImportStringList = new ArrayList<>();
        if (importList != null) {
            for (int i = 0; i < importList.size(); i++) {
                if (importList.get(i).toString().contains("*")) {
                    String str = importList.get(i).toString();
                    int index = str.indexOf("import");
                    str = str.substring(index);
                    String[] strs = str.split(" ");
                    str = strs[strs.length - 1];//得到Import的包的信息
                    str = str.replace(" ", ""); //替换掉空格" "
                    str = str.replace(";", ""); //去除;
                    starImportStringList.add(str);
                }
            }
        }
        //开始分析程序
        if (cu.getTypes() != null) {
            for (TypeDeclaration type : cu.getTypes()) {
                if (type instanceof ClassOrInterfaceDeclaration) {
                    //处理field
                    List<VariableDeclarationExpr> fieldExpressionList = new ArrayList<>();
                    for (BodyDeclaration body : type.getMembers()) {
                        if (body instanceof FieldDeclaration) {
                            FieldDeclaration field = (FieldDeclaration) body;
                            for (int i = 0; i < field.getVariables().size(); i++) {
                                VariableDeclarationExpr expr = new VariableDeclarationExpr();
                                List list = new ArrayList();
                                list.add(field.getVariables().get(i));
                                expr.setType(field.getType());
                                expr.setVars(list);
                                fieldExpressionList.add(expr);
                            }
                        }
                    }
                    //处理method
                    for (BodyDeclaration body : type.getMembers()) {
                        if (body instanceof MethodDeclaration) {
//                            try {
//                                if (!((MethodDeclaration) body).toString().contains("/*hole*/")) {
//                                    return null;
//                                }
//                            }catch(Exception e){
//                                return null;
//                            }catch(Error e){
//                                return null;
//                            }
                            int lines = countCodeLine(body);
                            if (lines >= 1) {
                                List<String> completeClassNameList = new ArrayList<>();
                                for (String str : tempList) {
                                    completeClassNameList.add(str);
                                }
                                List userClassList = new ArrayList();
                                for (String str : japaAst.getFilternames()) {
                                    userClassList.add(str);
                                }
                                UserClassProcessing userClassProcessing = new UserClassProcessing();
                                userClassProcessing.setUserClassList(userClassList);
                                userClassProcessing.setJdkList(jdkList);
                                userClassList.add("userDefinedClass");
                                MethodDeclaration method = (MethodDeclaration) body;
                                //System.out.println("---------------------------------------------");
                                //System.out.println(count + ": " + method.getName() + (method.getParameters() == null ? "[]" : method.getParameters()) + " (" + filePath + ") ");
                                //System.out.println("---------------------------------------------");
                                List<String> parameterNameList = new ArrayList<>();
                                List<String> typeMapList = new ArrayList<>();
                                List<String> completeTypeMapList = new ArrayList<>();
                                List<ExpressionStmt> parameterExpressionList = new ArrayList<>();
                                if (method.getParameters() != null) {
                                    List<Parameter> parameterList = method.getParameters();
                                    for (int i = 0; i < parameterList.size(); i++) {
                                        String contentString = "public class Test{public void test(){$}}";
                                        String parameterString = parameterList.get(i).toString() + ";";
                                        contentString = contentString.replaceAll("\\$", parameterString);
                                        InputStream in = new ByteArrayInputStream(contentString.getBytes());
                                        try {
                                            CompilationUnit compilationUnit = JavaParser.parse(in);
                                            Node node = compilationUnit.getTypes().get(0).getMembers().get(0);
                                            ExpressionStmt expression = (ExpressionStmt) node.getChildrenNodes().get(1).getChildrenNodes().get(0);
                                            parameterExpressionList.add(expression);
                                        } catch (Exception e) {
                                            continue;
                                        } catch (Error e) {
                                            continue;
                                        }
                                        // String[] strings = parameterList.get(i).toString().split(" ");
                                        // parameterNameList.add(strings[strings.length - 1]);
                                        // typeMapList.add(strings[strings.length - 1] + " " + parameterList.get(i).getType().toString());
                                        //completeTypeMapList.add(parameterList.get(i).getType().toString());
                                    }
                                }
                    /*添加类中的成员变量*/
                                SimplifiedTreeCreator creator = new SimplifiedTreeCreator(globalPath);
                                creator.setUserClassProcessing(userClassProcessing);
                                creator.setStarImportStringList(starImportStringList);
                                List<String> tempUserClassList = new ArrayList<>();
                                for (int i = 0; i < completeClassNameList.size(); i++) {
                                    try {
                                        Class clazz = Thread.currentThread().getContextClassLoader().loadClass(completeClassNameList.get(i));
                                        if (jdkList.contains(completeClassNameList.get(i))) {
                                            creator.getClass_name_map().put(clazz.getSimpleName(), completeClassNameList.get(i));
                                        } else {
                                            tempUserClassList.add(completeClassNameList.get(i));
                                            userClassList.add(completeClassNameList.get(i));
                                        }
                                    } catch (Exception e) {
                                        tempUserClassList.add(completeClassNameList.get(i));
                                        userClassList.add(completeClassNameList.get(i));
                                    } catch (Error e) {
                                        //System.err.println(e.getCause());
                                        tempUserClassList.add(completeClassNameList.get(i));
                                        userClassList.add(completeClassNameList.get(i));
                                    }

                                }
                                //过滤掉反射不到的类
                                for (int i = 0; i < tempUserClassList.size(); i++) {
                                    completeClassNameList.remove(tempUserClassList.get(i));
                                }
                                tempUserClassList.removeAll(tempUserClassList);
                                //处理field
                                for (int i = 0; i < fieldExpressionList.size(); i++) {
                                    creator.convert(fieldExpressionList.get(i));
                                }
                                //处理method中的parameter
                                for (int i = 0; i < parameterExpressionList.size(); i++) {
                                    creator.convert(parameterExpressionList.get(i));
                                }
                    /*get code tree from japa parse*/
                                CodeTree codeTree = constructTreeFromAST(completeClassNameList, parameterNameList, typeMapList,
                                        completeTypeMapList, starImportStringList, method, creator, userClassProcessing, holeFlag, globalPath, jdkList,
                                        gloveVocabList,stopWordsList);
                                if (codeTree != null && codeTree.getRoot() != null && codeTree.getTotalNumber() <= 1574) {
                    /*display the code tree*/
//                                    displayTree(codeTree, true, method.getName() + (method.getParameters() == null ? "[]" : method.getParameters()));
//                                    try {
//                                       //TreeWriter.print(codeTree.getRoot(), "graph/2.dot");
//                                    }catch(Exception e){
//                                        e.printStackTrace();
//                                    }
                                    //displayTree(codeTree,false);
                    /*store the code tree in mongodb*/
                                    //storeTreeInDB(codeTree);
                                    String treeSentence = toTreeStringFormat(codeTree,true);
                                    treeSentence = treeSentence.replace(" ","");
                                    return treeSentence;
                    /*construct training tree data */
                                } else {
                                    return null;
                                    //System.err.println("So " + method.getName() + (method.getParameters() == null ? "[]" : method.getParameters()) + " (" + filePath + ") " + " can not be correctly parsed");
                                }
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    public CodeTree constructTreeFromAST(List<String> completeClassNameList, List<String> parameterNameList,
                                         List<String> typeMapList, List<String> completeTypeMapList,
                                         List<String> starImportStringList, MethodDeclaration method,
                                         SimplifiedTreeCreator fieldCreator, UserClassProcessing userClassProcessing,
                                         boolean holeFlag, String globalPath, List<String> jdkList,
                                         List<String> gloveVocabList, List<String> stopWordsList ) {
        try {
            SimplifiedTreeCreator creator = new SimplifiedTreeCreator(completeClassNameList, fieldCreator, globalPath, jdkList);
            creator.setHoleFlag(holeFlag);
            for (int i = 0; i < parameterNameList.size(); i++) {
                creator.addClass_variable_list(parameterNameList.get(i));
            }
            for (int i = 0; i < typeMapList.size(); i++) {
                String[] strings = typeMapList.get(i).split(" ");
                creator.addClass_variable(strings[0], strings[1]);
            }
            for (int i = 0; i < completeTypeMapList.size(); i++) {
                creator.addClass_name_map(completeTypeMapList.get(i));
            }
            creator.setStarImportStringList(starImportStringList);
            creator.setUserClassProcessing(userClassProcessing);
            creator.toCodeTree(method);
            CodeTree codeTree = new CodeTree();
            codeTree.setRoot(creator.getCodeTree().getRoot());
            if(creator.getParsedFlag() && codeTree.getRoot() != null) {
                //移除没有用到的变量声明结点
                Map<String, List<TreeNode>> map = creator.getVariableNodeMap();
                for (String key : map.keySet()) {
                    List<TreeNode> list = map.get(key);
                    for (int i = 0; i < list.size(); i++) {
                        if ((list.get(i).isVariableDeclaration() && list.get(i).isPrimitive() && !list.get(i).isVariablePreserved())
                                || list.get(i).isAssign()) {
                            if (!codeTree.removeNode(list.get(i))) {
                                return null;
                            }
                        }
                    }
                }
                //添加类属性变量和函数声明中的变量
//                codeTree.getRoot().setPreviousVariableNames(creator.getUsedClassFieldAndMethodArgumentVariable());
//                if (codeTree.getRoot().getPreviousVariableNames() != null) {
//                    for (int i = 0; i < codeTree.getRoot().getPreviousVariableNames().size(); i++) {
//                        String variableName = codeTree.getRoot().getPreviousVariableNames().get(i);
//                        variableName = variableName.replaceAll("\r", "");
//                        variableName = variableName.replaceAll("\n", "");
//                        codeTree.getRoot().getPreviousVariableNames().set(i, variableName);
//                    }
//                }
                //加入变量名
//                codeTree.processRootVariables(codeTree.getRoot(), gloveVocabList, stopWordsList);
//                List<String> duplicatedVariableList = new ArrayList<>();
//                codeTree.initPreviousVariables(codeTree.getRoot(), duplicatedVariableList, gloveVocabList, stopWordsList);
                // codeTree.dealHoleParentNode(codeTree.getRoot());
                //加入注释
                //codeTree.initCommentList(codeTree.getRoot(), creator.getCommentList());
//                for(int i = 0; i < creator.getCommentList().size(); i ++){
//                    System.out.println(creator.getCommentList().get(i));
//                }
                //过滤掉原始语句中的\r\n符号
//                codeTree.filterSpecialCharacterInOriginalStatement(codeTree.getRoot());
                return codeTree;
            }
            else {
                return null;
            }
        } catch (Exception e) {
            //e.printStackTrace();
            return null;
        } catch (Error e) {
            //e.printStackTrace();
            return null;
        }
    }

    public void displayTree(CodeTree codeTree, boolean isCompleteFlag, String title) {
        TreeView treeView = new TreeView();
        treeView.convertCodeTree(codeTree, isCompleteFlag);
        DisplayTreeView display = new DisplayTreeView(treeView.getTree(), title);
    }


    public int countCodeLine(Node node) {
        int result = 0;
        if (node instanceof Statement && !(node instanceof BlockStmt)) {
            result += 1;
            if(node instanceof IfStmt){
                if(((IfStmt) node).getElseStmt() != null && !(((IfStmt) node).getElseStmt() instanceof IfStmt)){
                    result += 1;
                }
            }else if(node instanceof TryStmt){
                if(((TryStmt) node).getFinallyBlock() != null){
                    result += 1;
                }
            }
        }else if(node instanceof CatchClause){
            result += 1;
        }
        if (node.getChildrenNodes() != null) {
            for (int i = 0; i < node.getChildrenNodes().size(); i++) {
                result += countCodeLine(node.getChildrenNodes().get(i));
            }
        }
        return result;
    }


    public String toTreeStringFormat(CodeTree codeTree, boolean isCompleteFlag){
        CodeTreeOperation operation = new CodeTreeOperation();
        operation.setSerialNumberofEachNode(codeTree);
        List<String> stringList = new ArrayList<String>();
        List<TreeNode> nodeList = new ArrayList<TreeNode>();
        nodeList.add(codeTree.getRoot());
        while (nodeList.size() > 0) {
            List<TreeNode> tempList = new ArrayList<TreeNode>();
            for (int i = 0; i < nodeList.size(); i++) {
                TreeNode node = nodeList.get(i);
                if(!isCompleteFlag) {
                    stringList.add(node.getSerialNumber() - 1, node.toString());
                }
                else{
                    stringList.add(node.getSerialNumber() - 1, node.getCompleteMethodDeclaration());
                }
                for (int j = 0; j < node.getChildNodes().size(); j++) {
                    tempList.add(node.getChildNodes().get(j));
                }

            }
            nodeList.removeAll(nodeList);
            nodeList = tempList;
        }
        String content = "";
        for (int i = 0; i < stringList.size(); i++) {
            content += stringList.get(i) + " ";
        }
        return content;
    }

}
